package com.jingxuan.service.impl;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.jingxuan.dto.response.RecommendProductsResponse;
import com.jingxuan.entity.User;
import com.jingxuan.mapper.RecommendMapper;
import com.jingxuan.mapper.RecommendMapper.ProductFeatureDto;
import com.jingxuan.mapper.UserMapper;
import com.jingxuan.service.RecommendService;
import com.jingxuan.service.UserBehaviorService;
import com.jingxuan.util.JsonUtil;
import com.jingxuan.util.JwtUtil;
import com.jingxuan.util.RankAlgorithm;
import com.jingxuan.util.ProductsRecommendAlgorithm;
import com.jingxuan.util.RankAlgorithm.ProductFeature;
import com.jingxuan.util.RankAlgorithm.RecommendationResult;
import com.jingxuan.util.RankAlgorithm.ScoredProduct;
import com.jingxuan.util.UserBehaviorAlgorithm;
import com.jingxuan.util.UserBehaviorAlgorithm.ProductCandidate;
import com.jingxuan.util.UserBehaviorAlgorithm.ProductRecommendation;
import com.jingxuan.util.UserBehaviorAlgorithm.PersonalizedRecommendationResult;
import com.jingxuan.util.UserBehaviorAlgorithm.GlobalBehaviorStats;
import com.jingxuan.util.UserBehaviorAlgorithm.UserBehaviorSummary;

import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;

/**
 * 推荐服务实现类
 */
@Slf4j
@Service
public class RecommendServiceImpl implements RecommendService {

    @Autowired
    private RecommendMapper recommendMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private UserBehaviorService userBehaviorService;

    private static final int DEFAULT_LIMIT = 20;
    private static final int MAX_LIMIT = 100;

    @Override
    public List<RecommendProductsResponse> getRecommendProducts(Integer limit) {
        log.info("开始获取推荐商品列表，limit={}", limit);
        int realLimit = normalizeLimit(limit);

        try {
            UserPreferenceContext userContext = getCurrentUserContext();
            log.info("用户上下文解析完成，userId={}, preferenceTags={}"
                , userContext.getUserId(), userContext.getPreferenceTags());

            List<ProductFeatureDto> productFeatureDtos = loadCandidateFeatures(userContext.getPreferenceTags(), realLimit);
            if (CollectionUtils.isEmpty(productFeatureDtos)) {
                log.warn("未查询到任何商品数据，返回空列表");
                return new ArrayList<>();
            }
            
            // 进一步过滤掉没有描述的商品（双重保险）
            List<ProductFeatureDto> filteredDtos = productFeatureDtos.stream()
                .filter(dto -> dto.getDescription() != null && !dto.getDescription().trim().isEmpty())
                .collect(Collectors.toList());
            
            if (filteredDtos.isEmpty()) {
                log.warn("没有找到有描述的商品数据，返回空列表");
                return new ArrayList<>();
            }
            
            log.info("过滤前商品数量: {}, 过滤后商品数量: {}",
                productFeatureDtos.size(), filteredDtos.size());
            productFeatureDtos = filteredDtos;

            Map<String, ProductFeatureDto> dtoMap = productFeatureDtos.stream()
                .collect(Collectors.toMap(ProductFeatureDto::getProductId, Function.identity(), (a, b) -> a, LinkedHashMap::new));

            List<ProductFeature> productFeatures = productFeatureDtos.stream()
                .map(this::convertToProductFeature)
                .collect(Collectors.toList());

            RecommendationResult recommendationResult = ProductsRecommendAlgorithm.recommend(
                productFeatures,
                userContext.getPreferenceTags().isEmpty() ? null : userContext.getPreferenceTags());

            log.info("基础推荐算法完成，共评分 {} 个商品", recommendationResult.getProducts().size());
            logWeights(recommendationResult);

            Map<String, ScoredProduct> scoredProductMap = recommendationResult.getProducts().stream()
                .collect(Collectors.toMap(sp -> sp.getFeature().getProductId(), Function.identity()));

            List<ProductCandidate> candidates = buildProductCandidates(scoredProductMap, dtoMap);

            UserBehaviorSummary behaviorSummary = userContext.getUserId() != null
                ? userBehaviorService.getUserBehaviorSummary(userContext.getUserId())
                : UserBehaviorSummary.builder().build();
            GlobalBehaviorStats globalStats = userBehaviorService.getGlobalBehaviorStats();

            PersonalizedRecommendationResult personalizedResult = UserBehaviorAlgorithm.personalize(
                behaviorSummary,
                candidates,
                globalStats,
                productId -> {
                    ScoredProduct scored = scoredProductMap.get(productId);
                    return scored != null ? scored.getBaseScore() : 60D;
                },
                LocalDateTime.now());

            List<ProductRecommendation> personalizedRecommendations = personalizedResult.getRecommendations();
            Map<String, ProductRecommendation> personalizedMap = personalizedRecommendations.stream()
                .collect(Collectors.toMap(ProductRecommendation::getProductId,
                    Function.identity(),
                    (a, b) -> a,
                    LinkedHashMap::new));

            List<String> finalProductOrder;
            if (!personalizedMap.isEmpty()) {
                finalProductOrder = personalizedMap.keySet().stream()
                    .limit(realLimit)
                    .collect(Collectors.toList());
            } else {
                finalProductOrder = recommendationResult.getProducts().stream()
                    .map(sp -> sp.getFeature().getProductId())
                    .limit(realLimit)
                    .collect(Collectors.toList());
            }

            return finalProductOrder.stream()
                .map(productId -> convertToResponse(productId, dtoMap, scoredProductMap, personalizedMap))
                .collect(Collectors.toList());

        } catch (Exception e) {
            log.error("获取推荐商品列表失败，执行降级方案", e);
            return getFallbackProducts(realLimit);
        }
    }

    private int normalizeLimit(Integer limit) {
        if (limit == null || limit <= 0) {
            return DEFAULT_LIMIT;
        }
        return Math.min(limit, MAX_LIMIT);
    }

    private List<ProductFeatureDto> loadCandidateFeatures(List<String> preferenceTags, int limit) {
        int fetchSize = Math.min(MAX_LIMIT * 2, Math.max(limit * 2, DEFAULT_LIMIT));
        List<ProductFeatureDto> dtos;
        if (!CollectionUtils.isEmpty(preferenceTags)) {
            dtos = new ArrayList<>(recommendMapper.getProductFeaturesByPreferences(preferenceTags, fetchSize));
            if (dtos.size() < fetchSize) {
                List<ProductFeatureDto> fallback = recommendMapper.getProductFeatures(fetchSize);
                Map<String, ProductFeatureDto> merged = new LinkedHashMap<>();
                dtos.forEach(dto -> merged.put(dto.getProductId(), dto));
                fallback.forEach(dto -> merged.putIfAbsent(dto.getProductId(), dto));
                dtos = new ArrayList<>(merged.values());
            }
        } else {
            dtos = recommendMapper.getProductFeatures(fetchSize);
        }
        return dtos != null ? dtos : new ArrayList<>();
    }

    private UserPreferenceContext getCurrentUserContext() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                return UserPreferenceContext.empty();
            }
            HttpServletRequest request = attributes.getRequest();
            String authHeader = request.getHeader("Authorization");
            if (!StringUtils.hasText(authHeader)) {
                return UserPreferenceContext.empty();
            }
            String token = authHeader.startsWith("Bearer ") ? authHeader.substring(7) : authHeader;
            if (!StringUtils.hasText(token) || !jwtUtil.validateToken(token)) {
                return UserPreferenceContext.empty();
            }
            Long userId = jwtUtil.getUserIdFromToken(token);
            if (userId == null) {
                return UserPreferenceContext.empty();
            }
            User user = userMapper.selectById(userId);
            List<String> tags = (user != null && StringUtils.hasText(user.getPreferences()))
                ? JsonUtil.jsonToList(user.getPreferences())
                : Collections.emptyList();
            return new UserPreferenceContext(userId, tags == null ? Collections.emptyList() : tags);
        } catch (Exception ex) {
            log.warn("获取用户偏好标签失败", ex);
            return UserPreferenceContext.empty();
        }
    }

    private ProductFeature convertToProductFeature(ProductFeatureDto dto) {
        return ProductFeature.builder()
            .productId(dto.getProductId())
            .price(dto.getPrice())
            .goodRate(dto.getPositiveRate() != null ? dto.getPositiveRate() : 0.5)
            .commentCount(dto.getCommentCount() != null ? dto.getCommentCount() : 0)
            .lastCommentTime(dto.getLastCommentTime())
            .productType(dto.getProductType())
            .build();
    }

    private List<ProductCandidate> buildProductCandidates(Map<String, ScoredProduct> scoredMap,
                                                           Map<String, ProductFeatureDto> dtoMap) {
        Map<String, Double> heatPercentileMap = buildHeatPercentileMap(scoredMap.values());
        return scoredMap.values().stream()
            .map(scored -> {
                String productId = scored.getFeature().getProductId();
                ProductFeatureDto dto = dtoMap.get(productId);
                return ProductCandidate.builder()
                    .productId(productId)
                    .productName(dto != null ? dto.getProductName() : null)
                    .price(dto != null ? dto.getPrice() : scored.getFeature().getPrice())
                    .priceScore(scored.getPriceScore() / 100.0D)
                    .reputationScore(scored.getReputationScore() / 100.0D)
                    .heatScore(scored.getHeatScore() / 100.0D)
                    .timelinessScore(scored.getTimelinessScore() / 100.0D)
                    .keywordTags(buildKeywordTags(dto))
                    .category(dto != null ? normalizeCategory(dto.getProductType())
                        : normalizeCategory(scored.getFeature().getProductType()))
                    .releaseTime(dto != null ? dto.getLastCommentTime() : scored.getFeature().getLastCommentTime())
                    .heatPercentile(heatPercentileMap.getOrDefault(productId, 0.5D))
                    .baseScore(scored.getBaseScore())
                    .build();
            })
            .collect(Collectors.toList());
    }

    private Map<String, Double> buildHeatPercentileMap(Collection<ScoredProduct> scoredProducts) {
        if (CollectionUtils.isEmpty(scoredProducts)) {
            return Collections.emptyMap();
        }
        List<ScoredProduct> sorted = scoredProducts.stream()
            .sorted(Comparator.comparingDouble(ScoredProduct::getHeatScore))
            .collect(Collectors.toList());
        Map<String, Double> percentileMap = new HashMap<>(sorted.size());
        int size = sorted.size();
        for (int i = 0; i < size; i++) {
            ScoredProduct scored = sorted.get(i);
            double percentile = (i + 1D) / size;
            percentileMap.put(scored.getFeature().getProductId(), percentile);
        }
        return percentileMap;
    }

    private Set<String> buildKeywordTags(ProductFeatureDto dto) {
        Set<String> tags = new HashSet<>();
        if (dto == null) {
            return tags;
        }
        if (StringUtils.hasText(dto.getProductType())) {
            tags.add(normalizeCategory(dto.getProductType()));
        }
        if (StringUtils.hasText(dto.getProductName())) {
            String[] tokens = dto.getProductName().split("[^\\p{L}\\p{N}]+");
            for (String token : tokens) {
                if (token != null) {
                    String normalized = token.trim().toLowerCase(Locale.ROOT);
                    if (normalized.length() >= 2) {
                        tags.add(normalized);
                    }
                }
            }
        }
        return tags;
    }

    private String normalizeCategory(String category) {
        return StringUtils.hasText(category) ? category.trim().toLowerCase(Locale.ROOT) : "unknown";
    }

    private RecommendProductsResponse convertToResponse(String productId,
                                                        Map<String, ProductFeatureDto> dtoMap,
                                                        Map<String, ScoredProduct> scoredMap,
                                                        Map<String, ProductRecommendation> personalizedMap) {
        ProductFeatureDto dto = dtoMap.get(productId);
        RecommendProductsResponse response = new RecommendProductsResponse();
        if (dto != null) {
            response.setProductId(dto.getProductId());
            response.setProductName(dto.getProductName());
            response.setPrice(dto.getPrice());
            response.setPlatformName(dto.getPlatformName());
            response.setImageUrl(dto.getPictureUrl());
            response.setDescription(dto.getDescription());
        } else {
            response.setProductId(productId);
        }

        ScoredProduct base = scoredMap.get(productId);
        ProductRecommendation personalized = personalizedMap != null ? personalizedMap.get(productId) : null;
        if (base != null && personalized != null) {
            log.debug("商品 {} 推荐得分：基础={}, 内容={}, 协同={}, 新颖性={}, 最终={}",
                productId,
                String.format("%.2f", base.getBaseScore()),
                String.format("%.2f", personalized.getContentScore()),
                String.format("%.2f", personalized.getCollaborativeScore()),
                String.format("%.2f", personalized.getNoveltyScore()),
                String.format("%.2f", personalized.getFinalScore()));
        } else if (base != null) {
            log.debug("商品 {} 推荐得分：最终={}, 基础={}, 偏好={}, 标签加权={}",
                productId,
                String.format("%.2f", base.getFinalScore()),
                String.format("%.2f", base.getBaseScore()),
                String.format("%.2f", base.getPreferenceScore()),
                String.format("%.2f", base.getTagBonus()));
        }
        return response;
    }

    private void logWeights(RecommendationResult result) {
        double[] entropyWeights = result.getEntropyWeights();
        log.info("权重 - 价格:{}, 口碑:{}, 热度:{}, 时效:{}",
            String.format("%.4f", entropyWeights[0]),
            String.format("%.4f", entropyWeights[1]),
            String.format("%.4f", entropyWeights[2]),
            String.format("%.4f", entropyWeights[3]));
    }

    private List<RecommendProductsResponse> getFallbackProducts(Integer limit) {
        try {
            log.info("使用降级方案获取商品列表");
            List<ProductFeatureDto> dtos = recommendMapper.getProductFeatures(limit);
            if (dtos == null) {
                return new ArrayList<>();
            }
            
            // 过滤掉没有描述的商品
            List<ProductFeatureDto> filteredDtos = dtos.stream()
                .filter(dto -> dto.getDescription() != null && !dto.getDescription().trim().isEmpty())
                .collect(Collectors.toList());
                
            log.info("降级方案 - 过滤前商品数量: {}, 过滤后商品数量: {}",
                dtos.size(), filteredDtos.size());
                
            return filteredDtos.stream()
                .limit(limit)
                .map(dto -> {
                    RecommendProductsResponse response = new RecommendProductsResponse();
                    response.setProductId(dto.getProductId());
                    response.setProductName(dto.getProductName());
                    response.setPrice(dto.getPrice());
                    response.setPlatformName(dto.getPlatformName());
                    response.setImageUrl(dto.getPictureUrl());
                    response.setDescription(dto.getDescription());
                    return response;
                })
                .collect(Collectors.toList());
        } catch (Exception e) {
            log.error("降级方案也失败了", e);
            return new ArrayList<>();
        }
    }

    private static final class UserPreferenceContext {
        private final Long userId;
        private final List<String> preferenceTags;

        private UserPreferenceContext(Long userId, List<String> preferenceTags) {
            this.userId = userId;
            this.preferenceTags = preferenceTags;
        }

        static UserPreferenceContext empty() {
            return new UserPreferenceContext(null, Collections.emptyList());
        }

        Long getUserId() {
            return userId;
        }

        List<String> getPreferenceTags() {
            return preferenceTags;
        }
    }
}
